#include "console.h"
#include "debug.h"
#include "gdt.h"
#include "idt.h"
#include "timer.h"
#include "pmm.h"
#include "vmm.h"

void _kernel_init();

// 开启分页机制后的全局mboot
multiboot_t *glb_mboot_ptr;

//开启分页机制后的内核栈
char kernel_stack[STACK_SIZE];
/**
 * 内核使用的临时页表和页目录
 * 该地址必须是页对齐的地址，内存0-640KB肯定是空闲的
 * 
 */
__attribute__((section(".init.data"))) pgd_t *pgd_tmp = (pgd_t *)0x1000;
__attribute__((section(".init.data"))) pgd_t *pte_low = (pgd_t *)0x2000;
__attribute__((section(".init.data"))) pgd_t *pte_high = (pgd_t *)0x3000;
/**
 * @brief 内核入口函数
 *
 */
__attribute__((section(".init.text"))) void _kernel_entry()
{
    pgd_tmp[0] = (uint32_t)pte_low | PAGE_PRESENT | PAGE_WRITE;
    pgd_tmp[PGD_INDEX(PAGE_OFFSET)] = (uint32_t)pte_high | PAGE_PRESENT | PAGE_WRITE;
    // 映射内核虚拟地址4MB到物理地址的前4MB
    int i;
    for (i = 0; i < 1024; i++)
    {
        pte_low[i] = (i << 12) | PAGE_PRESENT | PAGE_WRITE;
    }
    for (i = 0; i < 1024; i++)
    {
        pte_high[i] = (i << 12) | PAGE_PRESENT | PAGE_WRITE;
    }
    // 映射0x000000000-0x00400000的物理地址到虚拟地址0xC0000000-0xC0400000
    asm volatile("mov %0,%%cr3" ::"r"(pgd_tmp));
    // 寄存器cr0
    uint32_t cr0;
    // 启动分页，将cr0寄存器的分页位置设置为1
    // 获取cr0的值
    asm volatile("mov %%cr0, %0"
                 : "=r"(cr0));
    // 改写cr0的分页位置值
    cr0 |= 0x80000000;
    asm volatile("mov %0, %%cr0" ::"r"(cr0));
    // 切换内核的运行栈
    uint32_t kernel_stack_top = ((uint32_t)kernel_mem_start + STACK_SIZE) & 0xFFFFFFF0;
    asm volatile("mov %0,%%esp\n\t"
                 "xor %%ebp,%%ebp" ::"r"(kernel_stack_top));
    // 更新全局mboot的指针
    glb_mboot_ptr = mboot_ptr_tmp;
    // 内核初始化
    _kernel_init();
}
/**
 * @brief 内核初始化函数
 *
 */
void _kernel_init()
{
    //清理屏幕
    console_clear();
    // 开启调试
    init_debug();
    // 初始化全局描述符
    init_gdt();
    // 初始化中断描述符
    init_idt();
    // 初始化定时器
    init_timer(200);
    // 打印字符
    printk_color(rc_black, rc_green, "Hello,Os Kernel\n");

    // 打印内存信息
    printk("kernel in memory start: 0x%08X\n", kernel_mem_start);
    printk("kernel in memory end:   0x%08X\n", kernel_mem_end);
    printk("kernel in memory used:   %d KB\n\n", (kernel_mem_end - kernel_mem_start + 1023) / 1024);

    show_memory_map();
    init_pmm();

    printk_color(rc_black, rc_red, "\nThe Count of Physical Memory Page is: %u\n\n", physical_page_count);

    uint32_t allc_addr = NULL;
    printk_color(rc_black, rc_light_brown, "Test Physical Memory Alloc :\n");
    allc_addr = pmm_alloc_page();
    printk_color(rc_black, rc_light_brown, "Alloc Physical Addr: 0x%08X\n", allc_addr);
    allc_addr = pmm_alloc_page();
    printk_color(rc_black, rc_light_brown, "Alloc Physical Addr: 0x%08X\n", allc_addr);
    allc_addr = pmm_alloc_page();
    printk_color(rc_black, rc_light_brown, "Alloc Physical Addr: 0x%08X\n", allc_addr);
    allc_addr = pmm_alloc_page();
    printk_color(rc_black, rc_light_brown, "Alloc Physical Addr: 0x%08X\n", allc_addr);
    init_vmm();
    while (1)
    {
        asm volatile("hlt");
    }
}